/* benchmark guru interfaces */
#include <benchmark/fft_bench.h>
#include <Utilities/RandomGenerator.h>
#include <Configuration.h>
#include <mpi/communicator.h>
#include <mpi/collectives.h>
#include <Utilities/Timer.h>
using namespace APPNAMESPACE;

int main(int argc, char** argv)
{
  mpi::environment env(argc,argv);
  mpi::communicator mycomm;
  OhmmsInfo ohmms("fft_bench",mycomm.rank(),0,1);

  typedef  double real_type;
  typedef  complex<double> complex_type;

  int howmany=540;
  int niters=10;
  int fft_dim=12288;
  bool enable_real=false;
  bool enable_complex=false;
  unsigned plan_mode = FFTW_MEASURE;
  bool debug=false;
  bool print_details=false;

  int num_datasets=0;
#if defined(HAVE_LIBFFTW)
  num_datasets+=2;
#endif
#if defined(HAVE_LIBESSL)
  num_datasets+=2;
#endif

  //cout.setf(std::ios::scientific, std::ios::floatfield);

  int ic=0;
  while(ic<argc)
  {
    string a(argv[ic]);
    if(a.find("-s")<a.size()) 
      fft_dim=atoi(argv[++ic]);
    else if(a.find("-n")<a.size()) 
      howmany=atoi(argv[++ic]);
    else if(a.find("-i")<a.size()) 
      niters=atoi(argv[++ic]);
    else if(a.find("-complex")<a.size()) 
      enable_complex=true;
    else if(a.find("-real")<a.size()) 
      enable_real=true;
    else if(a.find("-debug")<a.size())  
      debug=true;
    else if(a.find("-print_details")<a.size())  
      print_details=true;
    else if(a.find("-h") < a.size())
    {
      app_log() << "usage --s fft-size --n num-ffts --i iterations "
           << " [--complex --real --debug --print_details]" << endl;
      app_log() << "defaults real only and disable debug :"
        << " --s " << fft_dim 
        << " --n " << howmany 
        << " --i " << niters 
        << endl;
      return 1;
    }
    ++ic;
  }

  if(!(enable_real||enable_complex)) enable_real=true;

  double mflops_c=(log(static_cast<double>(fft_dim))/log(2.))*5.*fft_dim*howmany*1e-6;
  double mflops_r=mflops_c*0.5;

  if(debug)
  {
    app_log() << "#  Testing FFT dim= " << fft_dim << " multiplicity= " << howmany 
      << " iterations= " << niters 
      << " MFLOPS(complex)= " << static_cast<int>(mflops_c)
      << endl;
  }

  vector<double> cpu_secs(num_datasets*2);
  vector<string> fft_names;

  int data_id=0;
#if defined(HAVE_LIBFFTW)
  if(enable_real)
  {
    fft_names.push_back("FFTW-1");
    FFT_BENCH<real_type,real_type,false,FFTW_ENG> single(fft_dim,howmany,fft_dim+2);
    single.setTitle("fftw single double");
    single.benchmark(niters);
    cpu_secs[data_id++]=single.cpu_secs(0);
    cpu_secs[data_id++]=single.cpu_secs(1);
    if(debug) single.debug(print_details);

    fft_names.push_back("FFTW-M");
    FFT_BENCH<real_type,real_type,true,FFTW_ENG> many(fft_dim,howmany,fft_dim+2);
    many.setTitle("fftw multiple double");
    many.benchmark(niters);
    cpu_secs[data_id++]=many.cpu_secs(0);
    cpu_secs[data_id++]=many.cpu_secs(1);
    if(debug) many.debug(print_details);
  }

  //if(enable_complex)
  //{
  //  FFT_BENCH<real_type,complex_type,false,FFTW_ENG> single(fft_dim,howmany,fft_dim);
  //  single.setTitle("fftw single dcomplex");
  //  single.benchmark(niters,mflops_c);
  //  if(debug) single.debug(print_details);

  //  FFT_BENCH<real_type,complex_type,true,FFTW_ENG> many(fft_dim,howmany,fft_dim);
  //  many.setTitle("fftw multiple dcomplex");
  //  many.benchmark(niters,mflops_c);
  //  if(debug) many.debug(print_details);
  //}
#endif

#if defined(HAVE_LIBESSL)
  if(enable_real)
  {
    fft_names.push_back("ESSL-1");
    FFT_BENCH<double,double,false,FFTESSL_ENG> single(fft_dim,howmany,fft_dim+2);
    single.setTitle("essl single double");
    single.benchmark(niters);
    cpu_secs[data_id++]=single.cpu_secs(0);
    cpu_secs[data_id++]=single.cpu_secs(1);
    if(debug) single.debug(print_details);

    fft_names.push_back("ESSL-M");
    FFT_BENCH<double,double,true,FFTESSL_ENG> many(fft_dim,howmany,fft_dim+2);
    many.setTitle("essl multiple double");
    many.benchmark(niters);
    cpu_secs[data_id++]=many.cpu_secs(0);
    cpu_secs[data_id++]=many.cpu_secs(1);
    if(debug) many.debug(print_details);
  }

  //if(enable_complex)
  //{
  //  FFT_BENCH<double,complex_type,false,FFTESSL_ENG> single(fft_dim,howmany,fft_dim);
  //  single.setTitle("essl single dcomplex");
  //  single.benchmark(niters,mflops_c);
  //  if(debug) single.debug(print_details);

  //  FFT_BENCH<double,complex_type,true,FFTESSL_ENG> many(fft_dim,howmany,fft_dim);
  //  many.setTitle("essl multiple dcomplex");
  //  many.benchmark(niters,mflops_c);
  //  if(debug) many.debug(print_details);
  //}
#endif

#if defined(HAVE_LIBMKL)
  if(enable_real)
  {
    fft_names.push_back("MKL-1");
    FFT_BENCH<real_type,real_type,false,FFTMKL_ENG> single(fft_dim,howmany,fft_dim+2);
    single.benchmark(niters);
    //cpu_secs[data_id++]=single.cpu_secs(0);
    //cpu_secs[data_id++]=single.cpu_secs(1);
    if(debug) single.debug(print_details);

    fft_names.push_back("MKL-M");
    FFT_BENCH<real_type,real_type,true,FFTMKL_ENG> many(fft_dim,howmany,fft_dim+2);
    many.benchmark(niters);
    //cpu_secs[data_id++]=many.cpu_secs(0);
    //cpu_secs[data_id++]=many.cpu_secs(1);
    if(debug) many.debug(print_details);
  }
#endif

  mpi::reduce(mycomm,cpu_secs);

  if(debug)
  {
    app_log() << "#fft_dim howmany ";
    for(int i=0; i<fft_names.size(); ++i) app_log() << " " << fft_names[i];
    app_log() << std::endl;
  }

  double mflops_fac=mflops_r*mycomm.size();
  app_log() << fft_dim << " " << howmany << " ";
  for(int i=0,ii=0; i<num_datasets; ++i,ii+=2)
    app_log() << mflops_fac/cpu_secs[ii] << " ";
  app_log() << "r2c" << endl;

  app_log() << fft_dim << " " << howmany << " ";
  for(int i=0,ii=1; i<num_datasets; ++i,ii+=2)
    app_log() << mflops_fac/cpu_secs[ii] << " ";
  app_log() << "c2r" << endl;

  return 0;
}

